package at.fhv.androideightpuzzle.application;

import java.io.IOException;
import java.util.Observable;
import java.util.Observer;

import at.fhv.androideightpuzzle.application.exceptions.JoinGameFailedException;
import at.fhv.androideightpuzzle.application.interfaces.IPlayPuzzleController;
import at.fhv.androideightpuzzle.domain.EightPuzzle;
import at.fhv.androideightpuzzle.domain.FinishedPlayer;
import at.fhv.androideightpuzzle.domain.Position;
import at.fhv.androideightpuzzle.domain.PuzzleBoard;
import at.fhv.androideightpuzzle.domain.SwapDirection;
import at.fhv.androideightpuzzle.network.ConnectionType;
import at.fhv.androideightpuzzle.network.NetworkSocket;
import at.fhv.androideightpuzzle.network.NetworkSocketFactory;
import at.fhv.androideightpuzzle.network.message.GameResultMessage;
import at.fhv.androideightpuzzle.network.message.IMessage;
import at.fhv.androideightpuzzle.network.message.JoinGameMessage;
import at.fhv.androideightpuzzle.network.message.PuzzleSolvedMessage;
import at.fhv.androideightpuzzle.network.message.StartPuzzleMessage;

public class MultiPlayerClientController extends Observable implements IPlayPuzzleController,Observer{
	
	private static MultiPlayerClientController m_instance;
	
	private PlayPuzzleController m_playPuzzleController;
	
	private String m_playerName;
	private NetworkSocket m_networkSocket;
	
	private FinishedPlayer[] m_ranking;
	
	private MultiPlayerClientController(){
		//m_networkSocket=NetworkSocketFactory.createNetworkSocket(ConnectionType.TCP);
		m_playPuzzleController=new PlayPuzzleController();
		
		m_ranking=null;
	}
	
	public static MultiPlayerClientController getInstance(){
		if(m_instance==null){
			m_instance=new MultiPlayerClientController();
		}
		
		return m_instance;
	}
	
	public static boolean hasInstance(){
		if(m_instance!=null){
			return true;
		}else{
			return false;
		}
	}
	
	public static void disposeInstance(){
		if(m_instance!=null){
			//dispose logic
			m_instance.deleteObservers();
			
			try{
				m_instance.m_networkSocket.close();
			}catch(Exception exc){}
			
			m_instance=null;
		}
	}
	
	public void joinGame(String playerName,String host,ConnectionType type) throws JoinGameFailedException{
		if((playerName!=null) && (playerName.equals("")==false)){
			m_playerName=playerName;
			
			try{
				//connect and send message
				m_networkSocket=NetworkSocketFactory.createNetworkSocket(type);
				m_networkSocket.addObserver(this);
				m_networkSocket.connect(host);
				
				/*m_networkSocket.sendMessage(new JoinGameMessage(m_playerName));
				
				setChanged();
				notifyObservers(IObserverArguments.JOIN_SUCCESSFUL);*/
			}catch(IOException exc){
				android.util.Log.e("MultiplayerClientController",exc.getMessage(),exc);
				
				throw new JoinGameFailedException();
			}
		}
	}
	
	private void sendMessage(IMessage message){
		if(message!=null){
			try{
				m_networkSocket.sendMessage(message);
			}catch(IOException exc){
				android.util.Log.e("MultiPlayerClientController",exc.getMessage(),exc);
				
				if(m_networkSocket.isAlive()==false){
					setChanged();
					notifyObservers(IObserverArguments.CONNECTION_LOST);
				}
			}
		}
	}
	
	public FinishedPlayer[] getRanking(){
		return m_ranking;
	}
	
	//interface implementation
	public void cancelPuzzle(){
		m_playPuzzleController.cancelPuzzle();
	}
	
	public boolean isFinished(){
		boolean finished=m_playPuzzleController.isFinished();
		
		if(finished){
			sendMessage(new PuzzleSolvedMessage(m_playerName,getDuration()));
		}
		
		return finished;
	}
	
	public void loadPuzzle(String puzzleId){
		m_playPuzzleController.loadPuzzle(puzzleId);
	}
	
	public void shuffle(){
		m_playPuzzleController.shuffle();
	}
	
	public void start() {
		m_playPuzzleController.start();
	}
	
	public int[][] swapEmptyPart(SwapDirection swapDirection){
		return m_playPuzzleController.swapEmptyPart(swapDirection);
	}
	
	public int[][] swapPart(Position position){
		return m_playPuzzleController.swapPart(position);
	}
	
	public long getDuration(){
		return m_playPuzzleController.getDuration();
	}
	
	public int[][] getImagePositions(){
		return m_playPuzzleController.getImagePositions();
	}
	
	public void update(Observable observable,Object argument){
		if(observable instanceof NetworkSocket){
			if(argument instanceof GameResultMessage){
				GameResultMessage grm=(GameResultMessage)argument;
				m_ranking=new FinishedPlayer[grm.getPlayerNames().length];
				
				for(int i=0; i<m_ranking.length; i++){
					m_ranking[i]=new FinishedPlayer(grm.getPlayerNames()[i],grm.getDurations()[i]);
				}
				
				setChanged();
				notifyObservers(IObserverArguments.GAME_FINISHED);
			}else if(argument instanceof StartPuzzleMessage){
				StartPuzzleMessage spm=(StartPuzzleMessage)argument;
				
				m_playPuzzleController.setEightPuzzle(new EightPuzzle(
						spm.getPuzzleId(),new PuzzleBoard(spm.getParts(),spm.getEndState(),spm.getEmptyPartNumber())));
				
				//
				android.util.Log.d("MultiplayerClientController","start puzzle from controller");
				//
				
				setChanged();
				notifyObservers(argument);
			}else if(argument.equals(IObserverArguments.JOIN_SUCCESSFUL)){
				try{
					m_networkSocket.sendMessage(new JoinGameMessage(m_playerName));
					
					setChanged();
					notifyObservers(IObserverArguments.JOIN_SUCCESSFUL);
				}catch(IOException exc){
					android.util.Log.e("MultiPlayerClientController",exc.getMessage(),exc);
				}
			}else if(argument.equals(IObserverArguments.SERVER_NOT_AVAILABLE)){
				setChanged();
				notifyObservers(IObserverArguments.SERVER_NOT_AVAILABLE);
			}
		}
	}
}
